package com.office.trigger.http;

import com.office.api.IAiService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.SystemPromptTemplate;
import org.springframework.ai.document.Document;
import org.springframework.ai.openai.OpenAiChatClient;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.PgVectorStore;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.ArrayNode;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.time.Duration;


/**
 * openai接口
 * @author 数字牧马人
 */
@Slf4j
@RestController()
@CrossOrigin("*")
@RequestMapping("/api/v1/openai/")
public class OpenAiController implements IAiService {

    @Resource
    private OpenAiChatClient chatClient;

    @Resource
    private PgVectorStore pgVectorStore;

    private final ObjectMapper objectMapper = new ObjectMapper();

    @RequestMapping(value = "generate", method = RequestMethod.GET)
    @Override
    public ChatResponse generate(@RequestParam("model") String model, @RequestParam("message") String message) {
        log.info("收到OpenAI生成请求 - 模型: {}, 消息: {}", model, message);
        return chatClient.call(new Prompt(
                message,
                OpenAiChatOptions.builder()
                        .withModel(model)
                        .build()
        ));
    }

    /**
     * curl http://localhost:8080/api/v1/openai/generate_stream?model=deepseek-ai/DeepSeek-R1-Distill-Qwen-7B&message=1+1
     */
    @RequestMapping(value = "generate_stream", method = RequestMethod.GET)
    @Override
    public Flux<ChatResponse> generateStream(@RequestParam("model") String model, @RequestParam("message") String message) {
        log.info("收到OpenAI流式生成请求 - 模型: {}, 消息: {}", model, message);
        return chatClient.stream(new Prompt(
                message,
                OpenAiChatOptions.builder()
                        .withModel(model)
                        .build()
        ));
    }

    @RequestMapping(value = "generate_stream_rag", method = RequestMethod.GET, produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    @Override
    public Flux<String> generateStreamRag(@RequestParam("model") String model, @RequestParam("ragTag") String ragTag, @RequestParam("message") String message) {
        log.info("收到OpenAI RAG流式生成请求 - 模型: {}, 知识库: {}, 消息: {}", model, ragTag, message);

        String SYSTEM_PROMPT = """
                你是一个专业的AI助手，专门负责基于知识库内容回答用户问题。
                
                📋 回答规则：
                1. 🔍 严格基于提供的文档内容回答问题
                2. ❌ 如果文档中没有相关信息，请明确说明："抱歉，在知识库中没有找到相关信息"
                3. ✅ 回答要准确、简洁、有条理
                4. 📝 如果文档内容不完整，请说明这一点
                5. 🇨🇳 始终使用中文回答
                6. 🎯 回答要有针对性，避免冗余信息
                7. 💡 如果可能，提供实用的建议或总结
                
                📚 检索到的文档内容：
                {documents}
                
                ❓ 用户问题：{question}
                
                💬 请基于上述文档内容，为用户提供专业、准确的回答：
                """;

        try {
            // 从向量数据库中检索相关文档
            SearchRequest request = SearchRequest.query(message)
                    .withTopK(8)  // 增加检索数量以获得更好的覆盖
                    .withFilterExpression("knowledge == '" + ragTag + "'");

            log.info("开始向量数据库检索 - 查询: {}, 知识库: {}", message, ragTag);
            List<Document> documents = pgVectorStore.similaritySearch(request);
            log.info("检索完成 - 找到 {} 个相关文档", documents.size());

            // 提取文档内容并添加来源信息
            String documentContent = documents.stream()
                    .map(doc -> {
                        String content = doc.getContent();
                        String metadata = doc.getMetadata() != null ?
                            doc.getMetadata().getOrDefault("source", "未知来源").toString() : "未知来源";
                        return String.format("📄 来源: %s\n%s", metadata, content);
                    })
                    .collect(Collectors.joining("\n\n---\n\n"));

            // 如果没有找到相关文档，返回友好的提示信息
            if (documentContent.trim().isEmpty()) {
                log.warn("知识库 '{}' 中没有找到相关文档", ragTag);
                String errorMessage = String.format("""
                    🤔 抱歉，在知识库「%s」中没有找到与您问题相关的信息。
                    
                    💡 建议：
                    1. 检查问题描述是否准确
                    2. 尝试使用不同的关键词
                    3. 联系管理员确认知识库内容
                    
                    您的问题：「%s」
                    """, ragTag, message);

                return Flux.just("data: " + createResponseJson(errorMessage) + "\n\n");
            }

            // 创建系统消息
            Message systemMessage = new SystemPromptTemplate(SYSTEM_PROMPT)
                    .createMessage(Map.of("documents", documentContent, "question", message));

            // 创建用户消息
            Message userMessage = new UserMessage(message);

            // 构建消息列表
            List<Message> messages = new ArrayList<>();
            messages.add(systemMessage);
            messages.add(userMessage);

            log.info("开始生成OpenAI RAG回答 - 使用 {} 个文档", documents.size());

            return chatClient.stream(new Prompt(
                    messages,
                    OpenAiChatOptions.builder()
                            .withModel(model)
                            .build()
            )).onBackpressureBuffer(1000) // 添加背压控制
            .flatMap(response -> {
                try {
                    // 安全的空值检查
                    if (response == null || response.getResult() == null ||
                        response.getResult().getOutput() == null) {
                        log.warn("收到空的OpenAI响应对象，跳过处理");
                        return Flux.empty();
                    }

                    String content = response.getResult().getOutput().getContent();

                    // 确保内容不为空且有意义
                    if (content != null && !content.trim().isEmpty()) {
                        String jsonResponse = "data: " + createResponseJson(content) + "\n\n";
                        return Flux.just(jsonResponse);
                    } else {
                        log.debug("OpenAI响应内容为空，跳过处理");
                        return Flux.empty();
                    }
                } catch (Exception e) {
                    log.error("处理OpenAI响应时发生错误", e);
                    return Flux.empty();
                }
            })
            .timeout(Duration.ofMinutes(5)) // 设置超时时间
            .doOnComplete(() -> log.info("OpenAI流式响应处理完成"))
            .doOnError(error -> log.error("OpenAI流式响应处理出错", error))
            .concatWith(Flux.just("data: [DONE]\n\n"));
        } catch (Exception e) {
            log.error("OpenAI RAG处理过程中发生错误", e);
            String errorMessage = String.format("""
                ❌ 抱歉，处理您的请求时发生了错误：
                
                🔍 错误详情：%s
                
                💡 建议：
                1. 请稍后重试
                2. 检查网络连接
                3. 联系技术支持
                """, e.getMessage());

            return Flux.just("data: " + createResponseJson(errorMessage) + "\n\n");
        }
    }

    /**
     * 创建符合前端期望格式的响应JSON
     */
    private String createResponseJson(String content) {
        try {
            ObjectNode result = objectMapper.createObjectNode();
            ObjectNode metadata = result.putObject("metadata");
            metadata.putNull("contentFilterMetadata");
            metadata.putNull("finishReason");

            ObjectNode output = result.putObject("output");
            output.put("messageType", "ASSISTANT");
            output.putObject("properties");
            output.put("content", content);
            output.putArray("media");

            ObjectNode root = objectMapper.createObjectNode();
            root.set("result", result);

            ObjectNode rootMetadata = root.putObject("metadata");
            ObjectNode usage = rootMetadata.putObject("usage");
            usage.put("promptTokens", 0);
            usage.put("generationTokens", 0);
            usage.put("totalTokens", 0);
            rootMetadata.putArray("promptMetadata");

            ObjectNode rateLimit = rootMetadata.putObject("rateLimit");
            rateLimit.put("requestsRemaining", 0);
            rateLimit.put("requestsLimit", 0);
            rateLimit.put("requestsReset", "PT0S");
            rateLimit.put("tokensLimit", 0);
            rateLimit.put("tokensRemaining", 0);
            rateLimit.put("tokensReset", "PT0S");

            ArrayNode results = root.putArray("results");
            ObjectNode resultItem = results.addObject();
            ObjectNode itemMetadata = resultItem.putObject("metadata");
            itemMetadata.putNull("contentFilterMetadata");
            itemMetadata.putNull("finishReason");

            ObjectNode itemOutput = resultItem.putObject("output");
            itemOutput.put("messageType", "ASSISTANT");
            itemOutput.putObject("properties");
            itemOutput.put("content", content);
            itemOutput.putArray("media");

            return objectMapper.writeValueAsString(root);
        } catch (Exception e) {
            log.error("生成JSON响应失败", e);
            // 返回简单的错误响应
            return "{\"error\":\"JSON生成失败\",\"content\":\"" + content.replace("\"", "\\\"") + "\"}";
        }
    }

}
